package cn.mesmile.lock.service;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.Set;

/**
 * @author zb
 * @date 2020/12/4 14:37
 * @Description
 */
@Service
public class RedisService {


    private final RedisTemplate<String,Object> redisTemplate;

    public RedisService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }


    ///////////////////////////【Zset相关操作】//////////////////////////////
    // zadd zset  key value key2 value2
    // [zset是key] [key，key2是数字，排序依据] [value,value2是具体值]

    /**
     *  添加数据
     * @param key      这是key【key 不允许重复】
     * @param value    value
     * @param score    这是数字排序
     * @return  是否添加成功
     */
    public boolean zAdd(String key, Object value, double score) {
        Boolean result = redisTemplate.opsForZSet().add(key, value, score);
        Assert.notNull(result,">>>>>>>> RedisService----->zAdd 添加数据失败");
        return result;
    }

    /**
     *  通过下标范围来查找相关数据
     * @param key   key
     * @param indexStart 下标开始，从 0 开始
     * @param indexEnd   下标结束
     * @return 返回在下标范围内的所有 value
     */
    public Set<Object>  zRange(String key, long indexStart, long indexEnd){
        return this.zRange(key, indexStart, indexEnd, false);
    }


    /**
     *  通过下标范围来查找相关数据
     * @param key   key
     * @param indexStart 下标开始，从 0 开始
     * @param indexEnd   下标结束
     * @return 返回在下标范围内的所有 value  倒序
     */
    public Set<Object>  reverseZRange(String key, long indexStart, long indexEnd){
        return this.zRange(key, indexStart, indexEnd, true);
    }


    /**
     *  通过下标范围来查找相关数据
     * @param key   key
     * @param indexStart 下标开始，从 0 开始
     * @param indexEnd   下标结束
     * @param reverseOrder  是否倒序
     * @return 返回在下标范围内的所有 value
     */
    private Set<Object>  zRange(String key, long indexStart, long indexEnd,boolean reverseOrder) {
        Set<Object> range = null;
        if (reverseOrder) {
            range = redisTemplate.opsForZSet().reverseRange(key, indexStart, indexEnd);
        } else {
            range = redisTemplate.opsForZSet().range(key, indexStart, indexEnd);
        }
        Assert.notNull(range, ">>>>>>> RedisService----->zRange 查找数据失败");
        return range;
    }

    /**
     *  通过下标范围来查找相关数据
     * @param key       key
     * @param indexStart 下标开始，从 0 开始
     * @param indexEnd   下标结束
     * @return  返回在下标范围内的所有 key 和 value
     */
    public Set<ZSetOperations.TypedTuple<Object>> zRangeWithScores(String key, long indexStart, long indexEnd){
        Set<ZSetOperations.TypedTuple<Object>> typedTuples = redisTemplate.opsForZSet().rangeWithScores(key, indexStart, indexEnd);
        Assert.notNull(typedTuples,">>>>>>> RedisService----->zRangeWithScores 查询数据失败");
        return typedTuples;
    }

    /**
     *  查询某个分数范围内的数据
     * @param key  key
     * @param startScore   开始分数
     * @param endScore     结束分数
     * @return 返回一个 排序分数范围内的 所有value值
     */
    public Set<Object> zRangeBySource(String key, double startScore, double endScore) {
        Set<Object> objects = redisTemplate.opsForZSet().rangeByScore(key, startScore, endScore);
        Assert.notNull(objects,">>>>>>> RedisService----->zRangeBySource 查询数据失败");
        return objects;
    }

    /**
     * 取一定范围内的资源
     * @param key  这是key
     * @param startScore  这是开始的start的数字
     * @param endScore    这是结尾的end的数字
     * @param offset      从结果中下标截取 offset 开始截取  count 个元素
     * @param count       截取个数
     */
    public Set<Object> zRangeBySource(String key, double startScore, double endScore, long offset, long count){
        Set<Object> objects = redisTemplate.opsForZSet()
                .rangeByScore(key, startScore, endScore, offset, count);
        Assert.notNull(objects, ">>>>>>> RedisService----->zRangeBySourcePlus 查询数据失败");
        return objects;
    }

    /**
     * 删除指定key里面的多个 value
     * @param key       key
     * @param objects   多个value
     * @return   删除的个数
     */
    public long zRem(String key,Object... objects) {
        Long number = redisTemplate.opsForZSet().remove(key, objects);
        Assert.notNull(number, ">>>>>>> RedisService----->zRem 删除失败");
        return number;
    }


    /**
     *  查询key的个数
     * @param key   查询key的个数
     * @return
     */
    public long zCard(String key) {
        Long aLong = redisTemplate.opsForZSet().zCard(key);
        Assert.notNull(aLong, ">>>>>>> RedisService----->zCard 查询key的个数失败");
        return aLong;
    }


    /**
     * 统计 两个分数之间的key的个数
     * @param key   key
     * @param startScore    开始分数
     * @param endScore      结束分数
     * @return  返回两个分数之间的key的个数
     */
    public long zCount(String key,double startScore,double endScore){
        Long count = redisTemplate.opsForZSet().count(key, startScore, endScore);
        Assert.notNull(count,">>>>>>> RedisService----->zCount 查询key的个数失败" );
        return count;
    }


    /**
     *  通过value的值来获取，value所在的下标
     * @param key key
     * @param value value
     */
    public long zRank(String key, Object value) {
        Long rank = redisTemplate.opsForZSet().rank(key, value);
        Assert.notNull(rank, ">>>>>>> RedisService----->zRank value下标失败");
        return rank;
    }

    /**
     *  通过value的值来获取，value所在的下标
     * @param key key
     * @param value value
     */
    public long reverseZRank(String key, Object value) {
        Long rank = redisTemplate.opsForZSet().reverseRank(key, value);
        Assert.notNull(rank, ">>>>>>> RedisService----->zRank value下标失败");
        return rank;
    }

    /**
     *  通过value和key来获取 key 即 排序依据
     * @param key   key
     * @param object value
     */
    public double zScore(String key, Object object){
        Double score = redisTemplate.opsForZSet().score(key, object);
        Assert.notNull(score,">>>>>>> RedisService----->zScore 获取value的值失败");
        return score;
    }

    /**
     *  添加分数失败
     * @param key  key
     * @param value value
     * @param score 分数
     */
    public double zIncrby(String key,Object value,double score){
        // 给指定的值增加分数
        Double result = redisTemplate.opsForZSet().incrementScore(key, value, score);
        Assert.notNull(result, ">>>>>>> RedisService----->zIncrby 获取value的值失败");
        return result;
    }



}
